home *** CD-ROM | disk | FTP | other *** search
/ MacAddict 116 / MacAddict 116 (Mac Power Pack)(theDISC)(April 2006).iso / Software / Utilities / iGet2.0.3.dmg / iGet.app / Contents / Resources / transferdiff.pl < prev    next >
Encoding:
Perl Script  |  2006-01-19  |  9.4 KB  |  349 lines

  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use Getopt::Long;
  5.  
  6. # TODO!!!!!!!!
  7. # make the MacFileDiff and MacTransferDiff packages use
  8. # object syntax for variable access.
  9. # using $Package::variable doesn't warn if you type the variable name incorrectly!
  10.  
  11. $ENV{'PATH'} = '/Developer/Tools:.:' . $ENV{'PATH'};
  12.  
  13. my $skipPartialFiles;
  14. my $singleCompareMode;
  15.  
  16. GetOptions('c' => \$singleCompareMode,
  17.     'P' => \$skipPartialFiles);
  18.  
  19. my $result = 0;
  20.  
  21. if($singleCompareMode) {
  22.  
  23.     my $usage = "usage: $0 -c [-P] <file1> <file2>\n";
  24.  
  25.     my $file1 = shift @ARGV;
  26.     my $file2 = shift @ARGV;
  27.  
  28.     unless($file1 && $file2) {
  29.         die $usage;
  30.     }
  31.  
  32.     $result = MacFileDiff::compareNodes($file1, $file2, $skipPartialFiles);
  33. } else {
  34.  
  35.     my $usage = "usage: $0 <sourceFile1> <sourceFile2> ... <destinationDir>\n";
  36.  
  37.     my $targetDir = pop @ARGV;
  38.     my @sourceFiles = @ARGV;
  39.  
  40.     unless($targetDir && @sourceFiles) {
  41.         die $usage;
  42.     }
  43.  
  44.     $result = MacTransferDiff::transferDiff(@sourceFiles, $targetDir);
  45. }
  46.  
  47. exit($result);
  48.  
  49. package MacTransferDiff;
  50.  
  51. use strict;
  52. use File::Basename;
  53. use File::Find;
  54.  
  55. local @MacTransferDiff::skipPrefixes = ();
  56. local $MacTransferDiff::sourceFile = "";
  57. local $MacTransferDiff::baseName = "";
  58. local $MacTransferDiff::targetDir = "";
  59.  
  60. local $MacTransferDiff::result = 0;
  61.  
  62. sub transferDiff {
  63.  
  64.     $MacTransferDiff::sourceFile = "";
  65.     $MacTransferDiff::baseName = "";
  66.     @MacTransferDiff::skipPrefixes = ();
  67.     $MacTransferDiff::targetDir = pop @_;
  68.     my @sourceFiles = @_;
  69.     
  70.     if(!-d $MacTransferDiff::targetDir) {
  71.         print STDERR "$MacTransferDiff::targetDir is not a directory\n" unless -d ;
  72.         return 1;
  73.     }
  74.  
  75.     # append / to targetDir if necessary
  76.     if($MacTransferDiff::targetDir !~ /\/$/) { $MacTransferDiff::targetDir .= "/"; }
  77.  
  78.     foreach $MacTransferDiff::sourceFile (@sourceFiles) {
  79.         if(-d $MacTransferDiff::sourceFile) {
  80.             my $lastDir = $MacTransferDiff::sourceFile;
  81.             $lastDir =~ s/\/$//;
  82.             $MacTransferDiff::baseName = basename($lastDir);
  83.             if($MacTransferDiff::sourceFile !~ /\/$/) { $MacTransferDiff::sourceFile .= "/"; }
  84.             find({ wanted => \&findResult, no_chdir => 1 }, $MacTransferDiff::sourceFile);
  85.         } else {
  86.             macDiff($MacTransferDiff::sourceFile, basename($MacTransferDiff::sourceFile));
  87.         }
  88.     }
  89.     
  90.     return $MacTransferDiff::result;
  91.  
  92. }
  93.  
  94. sub findResult {
  95.  
  96.     my $subpath = $File::Find::name;
  97.     
  98.     if(-l $subpath) {
  99.         # if its a link, it may also show up as a directory,
  100.         # a file, or NOT a file if a dangling symlink.  In any of
  101.         # these cases, we want to call macDiff on it.
  102.     } else {
  103.         if(-d $subpath . "/Contents/ContainedPackage") {
  104.             # print "Skipping all paths under $subpath\n";
  105.             push(@MacTransferDiff::skipPrefixes, $subpath);
  106.         }
  107.         
  108.         if(-d $subpath && ($subpath !~ /\/$/)) { $subpath .= "/"; }
  109.         
  110.         unless(-r $subpath) {
  111.             print STDERR "$0 skipping $subpath because we can't read the source path\n";
  112.             return;
  113.         }
  114.     }
  115.     
  116.     foreach my $skip (@MacTransferDiff::skipPrefixes) {
  117.         if($subpath =~ /^\Q$skip/) {
  118.             # print "skipping subfile $subpath\n";
  119.             return;
  120.         }
  121.     }
  122.  
  123.     if($subpath eq (substr($MacTransferDiff::sourceFile, 0, length($MacTransferDiff::sourceFile)-1))) {
  124.         # weird case... we find using the full path with / at the end
  125.         # find returns the topmost node without the / for invalid symlink
  126.         # we can't call macDiff with the / at the end, because:
  127.         # invalid_symlink is ok
  128.         # invalid_symlink/ is not
  129.         macDiff($subpath, $MacTransferDiff::baseName);
  130.     } else {
  131.         # gotta use \Q to disable interpretaion of {} () etc
  132.         $subpath =~ s/^\Q$MacTransferDiff::sourceFile//;
  133.         macDiff($MacTransferDiff::sourceFile . $subpath, $MacTransferDiff::baseName . "/" . $subpath);
  134.     }
  135. }
  136.  
  137. sub macDiff {
  138.     # sourcefile is full path of source
  139.     # subfile is the node that should exist under targetdir
  140.     my ($sourceFile, $subFile) = @_;
  141.     my $targetFile = $MacTransferDiff::targetDir . $subFile;
  142.     # print STDERR "SOURCE $sourceFile TARGET $targetFile\n";
  143.     if(MacFileDiff::compareNodes($sourceFile, $targetFile, 1)) { $MacTransferDiff::result = 1; }
  144. }
  145.  
  146. package MacFileDiff;
  147.  
  148. use strict;
  149.  
  150. local $MacFileDiff::headerPrinted = 0;
  151. local $MacFileDiff::file1 = "";
  152. local $MacFileDiff::file2 = "";
  153. local $MacFileDiff::rezHack = "/..namedfork/rsrc";
  154. local $MacFileDiff::indent = "  ";
  155.  
  156. sub compareNodes {
  157.  
  158.     $MacFileDiff::headerPrinted = 0;
  159.  
  160.     my $result;
  161.     $MacFileDiff::file1 = shift @_;
  162.     $MacFileDiff::file2 = shift @_;
  163.     my $skipPartialFiles = shift @_;
  164.  
  165.     if(-l $MacFileDiff::file3 || -l $MacFileDiff::file2) {
  166.         my $result = compareSymlinks($MacFileDiff::file1, $MacFileDiff::file2);
  167.         return($result);
  168.     }
  169.  
  170.     if(!-e $MacFileDiff::file1) {
  171.         print STDERR "$MacFileDiff::file1 does not exist\n";
  172.         return 1;
  173.     }
  174.  
  175.     if(!-e $MacFileDiff::file2) {
  176.         print STDERR "$MacFileDiff::file2 does not exist\n";
  177.         return 1;
  178.     }
  179.  
  180.     if (-d $MacFileDiff::file1 || -d $MacFileDiff::file2) {
  181.         my $result = compareDirectories($MacFileDiff::file1, $MacFileDiff::file2);
  182.         return($result);
  183.     }
  184.  
  185.     ########### Metadata
  186.  
  187.  
  188.     my $metadata1 = readMetadata($MacFileDiff::file1);
  189.     my $metadata2 = readMetadata($MacFileDiff::file2);
  190.     
  191.     if($skipPartialFiles) {
  192.         foreach($metadata1, $metadata2) {
  193.             if(/type: "iGpf"/ && /creator: "iGet"/) {
  194.                 print STDERR "$0: Skipping partial file comparison\n";
  195.                 return 0;
  196.             }
  197.         }
  198.     }
  199.  
  200.     if($metadata1 ne $metadata2) {
  201.         
  202.         my $m1 = "/tmp/macdiff.1." . $$;
  203.         my $m2 = "/tmp/macdiff.2." . $$;
  204.  
  205.         writeMetadata($metadata1, $m1);
  206.         writeMetadata($metadata2, $m2);
  207.  
  208.         my $metadataDifferences = `diff --suppress-common-lines -y -W 70 $m1 $m2`;
  209.         unlink $m1, $m2;
  210.  
  211.         if($metadataDifferences) {
  212.             printHeader();
  213.             print STDERR "Metadata differences:\n";
  214.             my @lines = split("\n", $metadataDifferences);
  215.             print STDERR $MacFileDiff::indent, join("\n" . $MacFileDiff::indent, @lines), "\n";
  216.             $result = 1;
  217.         }
  218.     }
  219.  
  220.     ########### Data fork
  221.  
  222.     compareFiles($MacFileDiff::file1, $MacFileDiff::file2, "Data fork", 1) || { $result = 1 };
  223.  
  224.     ########### Resource fork
  225.  
  226.     compareFiles($MacFileDiff::file1 . $MacFileDiff::rezHack,
  227.         $MacFileDiff::file2 . $MacFileDiff::rezHack, "Resource fork") || { $result = 1 };
  228.  
  229.     $MacFileDiff::headerPrinted && printBar();
  230.  
  231.     return $result;
  232. }
  233.  
  234. sub readMetadata {
  235.  
  236.     my ($inputFile, $tmpFile) = @_;
  237.  
  238.     my $metadataCommand = "GetFileInfo " . quotemeta($inputFile) . " | tail +2";
  239.     my $metadata = `$metadataCommand`;
  240.     $? && return("GetFileInfo returned error " . $inputFile);
  241.  
  242.     return $metadata;
  243. }
  244.  
  245. sub writeMetadata {
  246.     my ($data, $file) = @_;
  247.     open TMP, ">$file";
  248.     print TMP $data;
  249.     close TMP;
  250. }
  251.  
  252. sub compareFiles {
  253.     my ($localFile1, $localFile2, $title, $compareMode) = @_;
  254.  
  255.     my @differences;
  256.     my ($f1mode, $f1length) = (stat($localFile1))[2,7];
  257.     my ($f2mode, $f2length) = (stat($localFile2))[2,7];
  258.         
  259.     if ($f1length != $f2length) {
  260.         push @differences, "lengths ($f1length, $f2length)";
  261.     }
  262.     
  263.     system("cmp", "-s", $localFile1, $localFile2);
  264.     my $cmpResult = ($? >> 8);
  265.     
  266.     if($cmpResult) {
  267.         push @differences, "contents";
  268.     }
  269.  
  270.     if($compareMode && ($f1mode != $f2mode)) {
  271.         push @differences, "perms ($f1mode, $f2mode)";
  272.     }
  273.     
  274.     if(@differences) {
  275.         printHeader();
  276.         print STDERR $title, " diffs: ", join(", ", @differences), "\n";
  277.         return 0;
  278.     }
  279.     
  280.     return 1;
  281. }
  282.  
  283. sub printHeader {
  284.     $MacFileDiff::headerPrinted && return;
  285.     
  286.     printBar();
  287.     print STDERR "macdiff found differences between the following items:\n";
  288.     print STDERR $MacFileDiff::indent, $MacFileDiff::file1, "\n";
  289.     print STDERR $MacFileDiff::indent, $MacFileDiff::file2, "\n";
  290.     $MacFileDiff::headerPrinted = 1;
  291. }
  292.  
  293. sub printBar {
  294.     print STDERR "***************************************************************************\n";
  295. }
  296.  
  297. sub compareSymlinks {
  298.     my ($file1, $file2) = @_;
  299.     
  300.     if((-l $file1) != (-l $file2)) {
  301.         printHeader();
  302.         print STDERR "one is a symlink and the other isn't\n";
  303.         printBar();
  304.         return 1;
  305.     }
  306.     
  307.     my $dest1 = readlink($file1);
  308.     my $dest2 = readlink($file2);
  309.     if($dest1 ne $dest2) {
  310.         printHeader();
  311.         print ETDERR "symlink targets don't match:\n";
  312.         print $MacFileDiff::indent, "$file1 -> $dest1\n", $MacFileDiff::indent, "$file2 -> $dest2\n";
  313.         printBar();
  314.         return 1;
  315.     }
  316.     return 0;
  317. }
  318.  
  319. sub compareDirectories {
  320.     my ($dir1, $dir2) = @_;
  321.     
  322.     if((-d $dir1) != (-d $dir2)) {
  323.         printHeader();
  324.         print STDERR "one is a folder and the other isn't\n";
  325.         printBar();
  326.         return 1;
  327.     }
  328.     
  329.     my ($f1mode, $f1date) = (stat($dir1))[2,9];
  330.     my ($f2mode, $f2date) = (stat($dir2))[2,9];
  331.     
  332.     my @differences;
  333.     if($f1mode != $f2mode) {
  334.         push @differences, "perms ($f1mode, $f2mode)";
  335.     }
  336.     
  337.     if($f1date != $f2date) {
  338.         push @differences, "mod date ($f1date, $f2date)";
  339.     }
  340.     
  341.     if(@differences) {
  342.         printHeader();
  343.         print STDERR "differences: ", join(", ", @differences), "\n";
  344.         return 1;
  345.     }
  346.     
  347.     return 0;
  348. }
  349.